package impl

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	appConst "poem-server/comm/const"
	"poem-server/comm/global"
	"poem-server/comm/request"
	"poem-server/comm/response"
	"poem-server/mvc/dao/model"
	"poem-server/mvc/dao/query"
	"poem-server/mvc/service"
	"strconv"
)

type UserControllerImpl struct {
	UserService service.UserService
}

func NewUserControllerImpl() UserControllerImpl {
	return UserControllerImpl{}
}

// TODO 手机号验证码登录
func (u UserControllerImpl) LoginByPhone(ctx *gin.Context) {
	var loginVo request.PhoneVo
	err := ctx.ShouldBindJSON(&loginVo)
	if err != nil {
		response.ValidateFail(ctx, request.GetErrorMsg(loginVo, err))
		return
	}
	// TODO 查询账号是否注册
	userDao := query.Use(global.App.Mysql).User
	user, err2 := userDao.Where(userDao.Phone.Eq(loginVo.Phone)).First()
	if err2 != nil {
		response.BusinessFail(ctx, err.Error())
	}
	if user == nil {
		response.BusinessFail(ctx, "用户未注册，请注册后登录~")
	}

	// TODO 由电话号码查询redis 存储的验证码信息
	code := global.App.Redis.Get(appConst.LOGIN_CODE).Val()
	if len(code) == 0 { //说明没找到验证码，可能是没输对手机号，也可能验证码已经过期了
		response.BusinessFail(ctx, "验证码过期，请重新获取")
		return
	}
	if code != loginVo.Code {
		response.BusinessFail(ctx, "验证码错误，请重新输入")
		return
	}

	// TODO 登录成功，给当前用户颁发token证书
	token, err := service.JwtService.CreateToken(service.AppGuardName, user)
	if err != nil {
		response.TokenFail(ctx)
		return
	}
	response.Success(ctx, token)
}

// TODO 账号密码登录
func (u UserControllerImpl) LoginByUserName(ctx *gin.Context) {
	var loginVo request.UsernameVo
	err := ctx.ShouldBind(&loginVo)
	if err != nil {
		response.ValidateFail(ctx, request.GetErrorMsg(loginVo, err))
		return
	}
	// TODO 查询账号是否注册
	userDao := query.Use(global.App.Mysql).User
	count, err2 := userDao.Where(userDao.Username.Eq(loginVo.Username)).Count()
	if err2 != nil || count != 1 {
		response.Fail(ctx, 100, "账号未注册~")
		return
	}
	// TODO 查询账号状态是否禁用或删除

	// TODO 校验账号密码
	user := u.UserService.LoginByUserName(loginVo)

	// TODO 登录成功则颁发token
	if user == nil {
		response.Fail(ctx, appConst.PASSWORD_ERROR, "密码错误，登录失败~")
		return
	} else {
		tokenData, err := service.JwtService.CreateToken(service.AppGuardName, user)
		if err != nil {
			response.TokenFail(ctx) // 颁发失败
			return
		}
		response.Success(ctx, tokenData) // 颁发成功
	}
}

// TODO 手机号注册
func (u UserControllerImpl) RegisterByPhone(ctx *gin.Context) {
	var registerVo request.PhoneVo
	err := ctx.ShouldBind(&registerVo)
	if err != nil {
		response.ValidateFail(ctx, request.GetErrorMsg(registerVo, err))
	}
	success := u.UserService.RegisterByPhone(ctx, registerVo)
	if !success {
		response.Fail(ctx, appConst.PHONE_REGISTER, "账号已经被注册")
		return
	}
	response.Success(ctx, "注册成功")
}

// TODO 只能修改自己的信息，所以也不需要传用户id，系统已记录
func (u UserControllerImpl) UpdateInfo(ctx *gin.Context) {
	var userVo request.UserInfoVo
	err := ctx.ShouldBind(&userVo)
	if err != nil {
		response.ValidateFail(ctx, request.GetErrorMsg(userVo, err)) // 对于修改一般不做限制
		return
	}

	success := u.UserService.UpdateInfo(userVo)
	if !success {
		response.Error(ctx, appConst.UPDATE_ERROR)
		return
	}
	response.Success(ctx, "修改信息信息成功")
}

func (u UserControllerImpl) GetInfo(ctx *gin.Context) {
	var userId = ctx.Query("id")

	// 如果是查询别人的个人信息 则需要传id
	var userInfo model.User
	if len(userId) != 0 {
		userid, err := strconv.Atoi(userId)
		if err != nil {
			response.ValidateFail(ctx, request.GetErrorMsg(userid, err))
			return
		}
		userInfo = u.UserService.GetInfo(int32(userid))
	} else { // 如果不传id 说明查询自己的
		userInfo = u.UserService.GetInfo(global.UserNow.UserID)
	}
	response.Success(ctx, userInfo)
}

func (u UserControllerImpl) RegisterByUserName(ctx *gin.Context) {
	var registerVo request.UsernameVo
	err := ctx.ShouldBind(&registerVo)
	if err != nil {
		response.ValidateFail(ctx, request.GetErrorMsg(registerVo, err))
		return
	}
	success := u.UserService.RegisterByUserName(ctx, registerVo)
	if !success {
		response.Error(ctx, appConst.USERNAME_REGISTER)
		return
	}
	response.Success(ctx, "注册成功")
}

func (u UserControllerImpl) SendCode(ctx *gin.Context) {
	phone := ctx.Param("phone")
	success := u.UserService.SendCode(phone)
	if !success {
		response.Error(ctx, appConst.CODE_ERROR)
	}
}

// 由于token 是无状态的，用户登出后token也没过期
// 所以我们要在用户登出的时候把这个token加入到黑名单中，
// jwt中间件鉴权的时候要判断该token 是不是黑名单里的，如果是则授权失败，因为该用户已经登出(有个宽限时间一般很短)
// 注意黑名单一定要设置过期时间，否则将导致黑名单巨大的问题
func (u UserControllerImpl) Logout(ctx *gin.Context) {
	err := service.JwtService.JoinBlackList(ctx.Keys["token"].(*jwt.Token))
	if err != nil {
		response.BusinessFail(ctx, "登出失败")
		return
	}
	response.Success(ctx, nil)
}
